/*
 *  bump_main.cpp
 *  RTR
 *
 *  Created by Bernardo Franceschin on 5/26/11.
 *
 */

#include<stdio.h>
#include<stdlib.h>
#include"glew.h"
#include"glheader.h"
#include"vshader.h"
#include"vmatrix.h"
#include"vmesh.h"
#include"VSphere.h"
#include"vtorus.h"
#include"vcylinder.h"
#include"vtexture.h"
#include "manipulator.h"

#include<math.h>
#include"BMPLoader.h"

#define NMAPS 1
const char * bump_images[] = {
  "243-normal.bmp",
  //"185__normalmap.bmp",
  //"brickwork_normal-map.bmp",
  //"bumpmap_normalmap.bmp",
  //"Normalmap.bmp",
};

static VShader * shader;
static VShader * phong;
static VShader * bump_shader;
static VMatrix * vmatrix;
static VSphere * sphere;
static VCylinder * knot;
static VCylinder * c_sin;
static bool ellipse = false;
static VTorus  * torus;
static VGrid * flat;
#define NSHAPES 5
static VShape * shapes[NSHAPES];
static int shape_id=0;
static VManipulator* manip;
static VTexture * nmaps[4];
static int nmaps_id=0;
static bool wireframeEnabled;
static float lpos[4] = {0.0f, 0.0f, 0.0f, 1.0f};
static float a = 0;
static bool bg = false;

void Keyboard(unsigned char key, int x, int y)
{
	switch ( key )
	{
    case 'a':
      a+=.05;
      break;
    case 'A':
      a-=.05;
      break;
    case 'b':
      bg= !bg;
      break;
    case 'n':
      nmaps_id = (nmaps_id+1)%NMAPS;
      break;
    case 's':
      if(shape_id==0){
        if(!ellipse){
          ellipse = true;
        }
        else{
          ellipse = false;
          shape_id = (shape_id+1)%NSHAPES;
        }
      }
      else{
        shape_id = (shape_id+1)%NSHAPES;
      }
      break;
    case '9':
      shader = phong;
      break;
    case '0':
      shader = bump_shader;
      break;
    case 'w':
      wireframeEnabled = !wireframeEnabled;
      if(wireframeEnabled)
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      else   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
      break;      
		case 27:
		case 'q':
		case 'Q':
			exit(0);
	}
  glutPostRedisplay();
}

void Redraw ()
{
  if(bg)
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 
  else
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  int vp[4];
  glGetIntegerv(GL_VIEWPORT,vp);
  float xmin, xmax, ymin, ymax, zmin, zmax;
  shapes[shape_id]->GetBBox(&xmin, &xmax, &ymin, &ymax, &zmin, &zmax);
  shapes[shape_id]->SetVertId(VPOS);
  shapes[shape_id]->SetNormalId(VNORMAL);
  float xcenter = (xmin+xmax) / 2;
  float ycenter = (ymin+ymax) / 2;
  float zcenter = (zmin+zmax) / 2;
  float zdist = 2*(zmax-zmin);
  vmatrix->SetMode(VM_PROJECTION);
  vmatrix->LoadIdentity();
  vmatrix->Perspective(50.0f, (float)vp[2]/vp[3], 0.1f, 100.0f);
  vmatrix->SetMode(GL_MODELVIEW);
  vmatrix->LoadIdentity();
  manip->SetZCenter(zdist);
  manip->Load();
  vmatrix->LookAt(xcenter, ycenter, zcenter-zdist,
                  xcenter, ycenter, zcenter,
                  0.0f, 1.0f, 0.0f
                  );
  shader->SetAsCurrent();
  int  l_id = glGetUniformLocation(shader->GetPid(), "lightPos");
  if(l_id == -1){
    printf("lightPos not found.\n");
  }
  lpos[0] = sin(a)*20+xcenter;
  lpos[2] = cos(a)*20-zdist;
  lpos[1] = ycenter ;
  glUniform4fv(l_id, 1, lpos);
  if(ellipse)
    vmatrix->Scale(1.0,1.5,1.0);
  vmatrix->SetUniformVar(shader->GetPid());
  nmaps[nmaps_id]->Load();
  shapes[shape_id]->Draw();
  glutSwapBuffers();
}

static void Init (char * vs, char* fs)
{
  GLenum err = glewInit();
  if (GLEW_OK != err) { 
    fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
    exit(-1);
  }
  glEnable(GL_DEPTH_TEST);
  phong = new VShader();
  phong->SetVertFile(PHONGVS);
  phong->SetFragFile(PHONGFS);
  phong->CreateProgram();
  bump_shader = new VShader();
  bump_shader->SetVertFile(BUMP1VS);
  bump_shader->SetFragFile(BUMP1FS);
  bump_shader->CreateProgram();
  shader = bump_shader;
  vmatrix = new VMatrix();
  vmatrix->SetActTex(true);
  manip = VManipulator::Instance();
  manip->SetMatrixStack(vmatrix);
  torus = new VTorus(100,false,false);
  sphere = new VSphere(100,false);
  knot = new VCylinder(100,0.5);
  knot->KnotShape();
  knot->SetupTex(VTEX,1.0,3.0);
  knot->CalcTangentVec(VTANGENT,false);
  c_sin = new VCylinder(100,0.2);
  c_sin->SinShape();
  c_sin->SetupTex(VTEX);
  c_sin->CalcTangentVec(VTANGENT,false);
  flat = new VGrid(10);
  flat->FillCoordAndBox();
  flat->CalcNormals();
  flat->SetupTex(VTEX);
  sphere->SetupTex(VTEX,2.0);
  torus->SetupTex(VTEX,3.0);
  flat->CalcTangentVec(VTANGENT,false);
  sphere->CalcTangentVec(VTANGENT,false);
  torus->CalcTangentVec(VTANGENT,false);
  shapes[0] = sphere;
  shapes[1] = torus;
  shapes[2] = c_sin;
  shapes[3] = flat;
  shapes[4] = knot;
  static BMPClass bmp[NMAPS];
  for(int i=0;i<NMAPS;i++){
    BMPLoad(bump_images[i],bmp[i]);
    nmaps[i] = new VTexture((void *)bmp[i].bytes, bmp[i].width, bmp[i].height,GL_RGB,GL_UNSIGNED_BYTE);
  }
}

int main (int argc, char** argv)
{
  //printf("%s\n",argv[0]);
  int gac=0;
  glutInit(&gac, NULL);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA);
  glutInitWindowSize(800,800);
  glutCreateWindow("ShaderTest");
  glutDisplayFunc(Redraw);
  glutKeyboardFunc(Keyboard);
  Init(argv[1],argv[2]);
  glutMainLoop();
  return 0;
}
